/*
 * Copyright (c) 2007-2008, Thomas Krause (tho.krause@gmail.com)
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of gidoo nor the names of its contributors may be used to
 *       endorse or promote products derived from this software without specific
 *       prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.gidoo.cms.jcr;

import java.io.IOException;
import java.util.Calendar;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.gidoo.cms.GidooCMS;
import org.gidoo.cms.helper.Tools;
import org.gidoo.cms.login.GidooCMSSession;

/**
 * Handle editing of pages.
 * @author <a href="tho.krause@gmail.com">Thomas Krause</a>
 */
public class EditDataManager 
{
  
  /**
   * This will create a new either public or private draft for a page.
   * @param page The page to update.
   * @param isPrivate True if this should be a private draft.
   * @param title The new title.
   * @param content The content.
   * @param s The session.
   * @return True if successful.
   */
  public static boolean createDraft(Node page, boolean isPrivate,
    String title, String content,
    GidooCMSSession s)
  {
    if(page == null || s == null)
    {
      return false;
    }
    
    try
    {
      Node rootNode = s.getJcrRootNode();
      String convertedPath = Tools.path2DraftName(page.getPath().substring(1));
      Node drafts = rootNode.getNode("drafts");
      Node privateDrafts = rootNode.getNode("private_drafts");
      // check for an old draft and remove it necessary
      if(drafts.hasNode(convertedPath))
      {
        Node oldDraft = drafts.getNode(convertedPath);
        oldDraft.remove();
      }
      if(privateDrafts.hasNode(convertedPath))
      {
        Node oldDraft = privateDrafts.getNode(convertedPath);
        oldDraft.remove();
      }
      
      // create new draft
      Node parent = isPrivate ? privateDrafts : drafts;
      
      Node newDraft = parent.addNode(convertedPath, page.getPrimaryNodeType().getName()); 
      
      newDraft.setProperty("cms:xml", content == null ? "" : content);
      newDraft.setProperty("cms:title", title  == null ? "" : title);
      newDraft.setProperty("cms:editor", s.getUsername());
      newDraft.setProperty("cms:publisher", "");
      if(page.hasProperty("cms:created"))
      {
        newDraft.setProperty("cms:created", page.getProperty("cms:created").getDate());
      }
      else
      {
        newDraft.setProperty("cms:created", Calendar.getInstance());
      }
      newDraft.setProperty("cms:lastModified", Calendar.getInstance());
      
      parent.save();
      
      return true;
    }
    catch (RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));
    }
    return false;
  }
  
  
  /**
   * Add a new ressource as draft.
   * @param fileUpload The model.
   * @param mime The mime type, may be null if mime-type should be autodetected.
   * @param pageNode The page to add this draft to.
   * @param s The session.
   * @return The new ressource or null if an error occured.
   */
  public static Node addRessource(FileUpload fileUpload, String mime,
    Node pageNode,  GidooCMSSession s)
  {
    try
    {
      if(fileUpload == null || pageNode == null || s == null)
      {
        return null;
      }

      String pagePath = pageNode.getPath().substring(1);

      if(mime == null || "".equals(mime))
      {
        mime = fileUpload.getContentType();
      }

      Node drafts = s.getJcrRootNode().getNode("drafts");

      Node res = drafts.addNode(Tools.path2DraftName(pagePath + "/" + fileUpload.getClientFileName()), "cms:resource");

      res.setProperty("jcr:mimeType", mime);
      res.setProperty("cms:editor", s.getUsername());
      res.setProperty("cms:size", fileUpload.getSize());
      res.setProperty("jcr:data", fileUpload.getInputStream());
      res.setProperty("jcr:lastModified", Calendar.getInstance());

      return res;
    }
    catch(IOException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("ioexception"));
    }    
    catch(RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));
    }
    
    return null;
  }
  
  /**
   * Delete a resource.
   * @param res The resource. (either published or draft).
   * @param s The session.
   * @return True if successful.
   */
  public static boolean deleteRessource(Node res, GidooCMSSession s)
  {
    if(res == null || s == null)
    {
      return false;
      
    }
    try
    {
      String oldPath = res.getPath().substring(1);
      Node parent = res.getParent();
      if(parent.getName().equals("drafts"))
      {
        oldPath = Tools.draftName2Path(res.getName());
      }
      
      res.remove();      
      parent.save();      
      
      Logger.getLogger(GidooCMS.ACTION_LOG).log(Level.INFO,
        "DELETE ACTION: user \"" + s.getUsername() + "\" deleted " +
        "resource " + oldPath);
      
      return true;
    }
    catch (RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));
    }
    return false;
  }
  
  /**
   * Moves a page "up" in the order.
   * @param page The page.
   * @param s The session.
   * @return True if successful.
   */
  public static boolean pageUp(Node page, GidooCMSSession s)
  {
    if(page == null || s == null)
      return false;
    
    try
    {
      if("cms:section".equals(page.getPrimaryNodeType().getName()))
      {
        s.error(getI18N(s).getString("sectionnotallowed"));
        return false;
      }
      
      Node parent = page.getParent();
      NodeIterator it = parent.getNodes();
      
      Node lastNode = null;
      
      while (it.hasNext())
      {
        Node c = it.nextNode();
        if(c.getName().equals(page.getName()))
        {
          if(lastNode != null)
          {
            parent.orderBefore(page.getName(), lastNode.getName());
            parent.save();
          }
          break;
        }
        else if("cms:page".equals(c.getPrimaryNodeType().getName()))
        {
          lastNode = c;
        }
      }
      
      return true;
    }
    catch (RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));

    }
    
    return false;
  }
  
  /**
   * Moves a page "down" in the order.
   * @param page The page.
   * @param s The session.
   * @return True if successful.
   */
  public static boolean pageDown(Node page, GidooCMSSession s)
  {
    try
    {
      if("cms:section".equals(page.getPrimaryNodeType().getName()))
      {
        s.error(getI18N(s).getString("sectionnotallowed"));
        return false;
      }
      
      Node parent = page.getParent();
      NodeIterator it = parent.getNodes();
      
      while (it.hasNext())
      {
        Node c = it.nextNode();
        if(c.getName().equals(page.getName()))
        {
          Node nextCMSNode = null;
          while(it.hasNext())
          {
            Node n = it.nextNode();
            if("cms:page".equals(n.getPrimaryNodeType().getName()))
            {
              nextCMSNode = n;
              break;
            }
          }
          if(nextCMSNode != null)
          {
            parent.orderBefore(nextCMSNode.getName(), page.getName());
            parent.save();
          }
          break;
        }
      }
      
      return true;
    }
    catch (RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));

    }
    
    return false;
  }
  
  /**
   * Add a new page.
   * @param name The name of the new page.
   * @param parent The parent page.
   * @param s The session.
   * @return The new page. Null if an error occured.
   */
  public static Node addNewPage(String name, Node parent, GidooCMSSession s)
  {

    if(parent == null || s == null)
    {
      return null;
    }
    
    if(name != null && !"".equals(name))
    {
      try
      {
        
        // check for unallowed characters
        if(name.contains("/"))
        {
          s.error(getI18N(s).getString("noslashallowed"));
          return null;
        }
        if(JcrHelper.META_DATA_NODE_NAME.equals(name))
        {
          s.error(getI18N(s).getString("namereserved"));
          return null;
        }
        
        // check if already existant
        NodeIterator it = parent.getNodes();
        while(it.hasNext())
        {
          Node n = it.nextNode();
          if(n.getName().equals(name))
          {
            s.error(getI18N(s).getString("alreadyexists"));
            return null;
          }
        }
      
        // construct a new page
        Node p = parent.addNode(name, "cms:page");
        p.setProperty("cms:editor", s.getUsername());
        
        parent.save();
        
        return p;
      }
      catch(RepositoryException ex)
      {
        Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
        s.error(getI18N(s).getString("jcrerror"));
      }
    }
    else
    {
      s.error(getI18N(s).getString("namewasnull"));
    }
    
    return null;
  }
  
  /**
   * Delete an existing page.
   * @param page The page to delete.
   * @param s The session.
   * @return True if successful.
   */
  public static boolean deletePage(Node page, GidooCMSSession s)
  {
    if(page == null || s == null)
    {
      return false;
    }
    
    try
    {
      
      if("cms:section".equals(page.getPrimaryNodeType().getName()))
      {
        s.error(getI18N(s).getString("sectionnotallowed"));
      }      
      // we always have at least one node (cms:md), so check if there are more
      else if(page.getNodes().getSize() > 1)
      {
        s.error(getI18N(s).getString("nosubpagesallowed"));
      }
      else
      {
        Node parent = page.getParent();
        
        boolean pageEmpty = true;
        if(page.hasProperty("cms:xml") && 
          !page.getProperty("cms:xml").getString().equals(""))
        {
          pageEmpty = false;
        }
        if(page.hasProperty("cms:title") && 
          !page.getProperty("cms:title").getString().equals(""))
        {
          pageEmpty = false;
        }
        if(pageEmpty)
        {
          String oldPath = page.getPath();
          page.remove();
          parent.save();
          
          Logger.getLogger(GidooCMS.ACTION_LOG).log(Level.INFO, 
            "DELETE ACTION: user \"" + s.getUsername() + 
            "\" deleted page " + oldPath);
          
        }
        else
        {
          s.error(getI18N(s).getString("pagemustbeemptybeforedelete"));
        }
      }
      
      return true;
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));
    }
    
    return false;
    
  }
  
  /**
   * Rename a page.
   * @param page The page.
   * @param name Thew new name.
   * @param s The session.
   * @return The new page or null if an error occured.
   */
  public static Node renamePage(Node page, String newName, GidooCMSSession s)
  {
    if(page == null || s == null)
    {
      return null;
    }
    
    try
    {
      Node parent = page.getParent();

      if(newName == null || "".equals(newName))
      {
        s.error(getI18N(s).getString("namewasnull"));
      }
      else if("cms:section".equals(page.getPrimaryNodeType().getName()))
      {
        s.error(getI18N(s).getString("sectionnotallowed"));
      }
      else if(newName.contains("/"))
      {
        s.error(getI18N(s).getString("noslashallowed"));
      }
      else if(JcrHelper.META_DATA_NODE_NAME.equals(newName))
      {
        s.error(getI18N(s).getString("namereserved"));
      }
      else
      {
          // check if already existant
          NodeIterator it = parent.getNodes();
          while(it.hasNext())
          {
            Node n = it.nextNode();
            if(n.getName().equals(newName))
            {
              s.error(getI18N(s).getString("alreadyexists"));
              return null;
            }
          }
          
          String oldPath = page.getPath();
          Session session = s.getJcrSession();
          
          session.move(page.getPath(), parent.getPath() + "/" + newName);
          session.save();
          
          Logger.getLogger(GidooCMS.ACTION_LOG).log(Level.INFO, 
            "RENAME ACTION: user \"" + s.getUsername() + 
            "\" renamed page from " + oldPath + " to " + page.getPath());
          
          return page;
        }
      
    }
    catch (RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));
    }
    
    return null;
  }
  
  /**
   * Move a page.
   * @param page The page to move.
   * @param newParent The new parent.
   * @param s The session.
   * @return The new page or null if an error occured.
   */
  public static Node movePage(Node pageToMove, Node newParent, GidooCMSSession s)
  {
    if(pageToMove == null || newParent == null || s == null)
    {
      return null;
    }
    
    try
    {
      if("cms:section".equals(pageToMove.getPrimaryNodeType().getName()))
      {
        s.error(getI18N(s).getString("sectionnotallowed"));
        return null;
      }

      Node oldSection = JcrHelper.getSectionOfNode(pageToMove);
      Node newSection = JcrHelper.getSectionOfNode(newParent);

    
      if(!oldSection.getName().equals(newSection.getName()))
      {
        s.error(getI18N(s).getString("nooverlappaste"));
        return null;
      }

      if(pageToMove.getParent().equals(newParent))
      {
        return pageToMove;
      }

      String oldPath = pageToMove.getPath();

      Session session = s.getJcrSession();
      
      session.move(pageToMove.getPath(), newParent.getPath() + "/" + pageToMove.getName());
      session.save();

      Logger.getLogger(GidooCMS.ACTION_LOG).log(Level.INFO,
        "MOVE ACTION: user \"" + s.getUsername() + 
        "\" moved page " + oldPath + " to " + pageToMove.getPath());

      return pageToMove;
    }
    catch (RepositoryException ex)
    {
      Logger.getLogger(GidooCMS.ERROR_LOG).log(Level.SEVERE, null, ex);
      s.error(getI18N(s).getString("jcrerror"));
    }
    
    return null;
  }  
  
  private static ResourceBundle getI18N(GidooCMSSession session)
  {
    return java.util.ResourceBundle.getBundle("org/gidoo/cms/jcr/I18N", session.getLocale());
  }
  
}
